package com.yunji.hushuang.hsproxy;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @author hushuang
 * @since 2019/10/15
 */
public class HsProxy {

    private static final String LN = "\r\n";

    private static final String CLASS_NAME = "$HsProxy0";

    private HsProxy() {
    }

    // 返回代理对象
    @SuppressWarnings("unchecked")
    public static <T> T newProxyInstance(HsClassLoader classLoader, Class<T> targetClazz, InvocationHandler invocationHandler) {

        try {
            // 生成 Java 源码
            String source = generateSrc(targetClazz);
            // 将源码输出到 Java 文件
            File sourceFile = saveSrc(source);
            // 将 Java 文件编译成 class 文件
            compile(sourceFile);
            // 将 class 文件加载到 jvm
            Class<?> clazz = classLoader.findClass(CLASS_NAME);
            // 返回代理对象
            return (T) clazz.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
        } catch (Exception e) {
            throw new RuntimeException("Create Proxy Failed", e);
        }
    }

    private static void compile(File sourceFile) throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        try (StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null)) {
            Iterable iterable = manage.getJavaFileObjects(sourceFile);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
            task.call();
        } finally {
            sourceFile.delete();
        }
    }

    private static File saveSrc(String source) throws IOException {
        File sourceFile = new File(HsProxy.class.getResource("").getPath() + CLASS_NAME + ".java");
        try (FileWriter fileWriter = new FileWriter(sourceFile)) {
            fileWriter.write(source);
            fileWriter.flush();
        }
        return sourceFile;
    }

    private static String generateSrc(Class clazz) {
        StringBuilder sb = new StringBuilder();
        sb.append("package ").append(HsProxy.class.getPackage().getName()).append(";").append(LN)
                .append(LN)
                // 引包
                .append("import java.lang.reflect.Method;").append(LN)
                .append("import java.lang.reflect.InvocationHandler;").append(LN)
                .append(LN)
                // 创建类, 实现接口
                .append("public class ").append(CLASS_NAME).append(" extends ").append(clazz.getName()).append(" {").append(LN);

        // 字段
        sb.append("private InvocationHandler h;").append(LN)
                // 构建方法
                .append("public ").append(CLASS_NAME).append("(InvocationHandler h) {").append(LN)
                .append("this.h = h;").append(LN)
                .append("}").append(LN);

        for (Method m : clazz.getDeclaredMethods()) {
            if (Modifier.isFinal(m.getModifiers())) {
                continue;
            }

            sb.append("@Override").append(LN)
                    .append(Modifier.toString(m.getModifiers())).append(" ")
                    .append(m.getReturnType()).append(" ")
                    .append(m.getName()).append("() {")
                    .append(LN)

                    // 调用代理方法，并捕获异常
                    .append("try {").append(LN)
                    .append("Method m = ").append(clazz.getName()).append(".class.getMethod(\"").append(m.getName()).append("\", new Class[0]);").append(LN)
                    .append("h.invoke(this, m, new Object[0]);").append(LN)
                    .append("} catch (Throwable e) {").append(LN)
                    .append("e.printStackTrace();").append(LN)
                    .append("}").append(LN)
                    .append("}").append(LN);
        }
        sb.append("}").append(LN);

        return sb.toString();
    }
}
